﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Threading;

namespace Inovout.Caching
{
    public class DictionaryCache<TKey, TValue> : ICache<TKey, TValue>
    {
        private readonly IDictionary<TKey, TValue> dictionary;
        private readonly ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim();

        public DictionaryCache()
            : this(null)
        {
        }

        public DictionaryCache(IEqualityComparer<TKey> comparer)
        {
            dictionary = new Dictionary<TKey, TValue>(comparer);
        }

        public TValue AddOrGet(TKey key, Func<TValue> creator)
        {
            TValue result;
            if (TryGet(key, out result))
            {
                return result;
            }
            else
            {
                result = creator();
                Set(key, result);
                return result;
            }
        }
        public void Set(TKey key, TValue value)
        {
            readerWriterLock.EnterWriteLock();
            try
            {
                dictionary[key] = value;
            }
            finally
            {
                readerWriterLock.ExitWriteLock();
            }
        }
        public TValue Get(TKey key)
        {
            readerWriterLock.EnterReadLock();
            try
            {
                TValue result;
                if (dictionary.TryGetValue(key, out result))
                {
                    return result;
                }
            }
            finally
            {
                readerWriterLock.ExitReadLock();
            }
            throw new KeyNotFoundException();
        }
        public bool TryGet(TKey key, out TValue value)
        {
            readerWriterLock.EnterReadLock();
            try
            {
                if (dictionary.TryGetValue(key, out value))
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            finally
            {
                readerWriterLock.ExitReadLock();
            }
        }
        void ICache.Set(object key, object value)
        {
            Set((TKey)key, (TValue)value);
        }

        object ICache.Get(object key)
        {
            return Get((TKey)key);
        }
    }
}
